package com.gopay.bis.nucc.common.util;

import java.security.cert.X509Certificate;
import javax.annotation.PostConstruct;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectionKeepAliveStrategy;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;

/**
 * @description: 连接池管理类
 * @author: zhoutong
 * @datetime: 2017/08/21 10:44
 * @version: 1.0.0
 */
public class HttpClientConnManager {

    /**
     * 日志
     */
    private static Logger logger = LoggerFactory.getLogger(HttpClientConnManager.class);

    private ConnectionKeepAliveStrategy keepAliveStrategy;


    /**
     * httpclient连接池管理器
     */
    protected PoolingHttpClientConnectionManager connMgr = null;

    /**
     * 请求配置
     */
    protected RequestConfig requestConfig = null;

    /**
     * 连接超时时间
     */
    @Value("${httpclient.connect.timeout}")
    protected int connectTimeOut;

    /**
     * 读取超时时间
     */
    @Value("${httpclient.read.timeout}")
    protected int readTimeOut;

    /**
     * 连接池获取超时时间
     */
    @Value("${httpclient.request.timeout}")
    protected int requestTimeOut;

    /**
     * 连接超时时间
     */
    @Value("${httpclient.http.keepalive.timeout}")
    protected int httpKeepAliveTimeOut;

    /**
     * 连接池大小
     */
    @Value("${httpclient.conn.pool.size}")
    protected int connPoolSize;

    /**
     * 字符编码
     */
    protected static final String SEND_ENCODING = "UTF-8";

    private static final String[] supportedProtocols = new String[]{"TLSv1.2"};
    private static final String[] supportedCipherSuites = new String[]{"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_RSA_WITH_AES_256_GCM_SHA384"};

    /**
     * 项目启动时运行，初始化连接池及连接配置
     */
    @PostConstruct
    public void init() {
        SSLConnectionSocketFactory sslConnFactory = null;
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain, String authType) {
                    return true;
                }
            }).build();

            logger.info("创建sslContext成功，并设置信任所有证书");

            sslConnFactory = new SSLConnectionSocketFactory(sslContext, supportedProtocols,supportedCipherSuites,new HostnameVerifier() {
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });

            logger.info("创建sslConnFactory成功，并设置hostname校验为true");
        } catch (Exception e) {
            logger.error("初始化连接池失败", e);
            throw new RuntimeException();
        }

        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", sslConnFactory)
                .register("http", new PlainConnectionSocketFactory())
                .build();
        //设置连接池
        connMgr = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        //设置连接池大小
        connMgr.setMaxTotal(connPoolSize);
        connMgr.setDefaultMaxPerRoute(connMgr.getMaxTotal());

        RequestConfig.Builder configBuilder = RequestConfig.custom();
        //设置连接超时时间
        configBuilder.setConnectTimeout(connectTimeOut);
        //设置读取超时时间
        configBuilder.setSocketTimeout(readTimeOut);
        //设置从连接池获取连接实例超时时间
        configBuilder.setConnectionRequestTimeout(requestTimeOut);

        requestConfig = configBuilder.build();

        keepAliveStrategy = new GopayConnectionKeepAliveStrategy(httpKeepAliveTimeOut);

        logger.info("初始化连接池成功，并成功设置连接属性");
    }

    /**
     * 定时清除过期连接
     */
    @Scheduled(fixedRate = 15000)
    public void closeExpiredConnections() {
        logger.info("定时清除过期连接");
        connMgr.closeExpiredConnections();
    }

    /**
     * 获取httpclient连接
     *
     * @return CloseableHttpClient
     */
    protected CloseableHttpClient getHttpClient() {
        //创建httpclient
        CloseableHttpClient httpClient = HttpClients.custom()
                .setConnectionManager(connMgr)//设置连接池
                .disableAutomaticRetries()
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                .setKeepAliveStrategy(keepAliveStrategy)
                .build();
        logger.info("创建httpClient成功");
        return httpClient;
    }

    private static class GopayConnectionKeepAliveStrategy implements ConnectionKeepAliveStrategy{
        private int httpKeepAliveTimeout;

        public GopayConnectionKeepAliveStrategy (int httpKeepAliveTimeout) {
            this.httpKeepAliveTimeout = httpKeepAliveTimeout;
        }

        @Override
        public long getKeepAliveDuration(HttpResponse httpResponse, HttpContext httpContext) {
            return httpKeepAliveTimeout;
        }
    }
}
